﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Pavilion;

namespace Pavilion.GameTools
{
    public class KeyboardTool : GameToolBase 
    {
        public const string Name = "KeyboardTool";

        private IKeyboard keyboard;
        private Dictionary<Keys, NotifiableCollection<Action>> PressedKeyActions, ReleasedKeyActions;
        private DynamicIterator<Action> actionIterator;

        public KeyboardTool()
        {
            CreateKeyboard(PlayerKeyboard.Name);
            PressedKeyActions = new Dictionary<Keys, NotifiableCollection<Action>>();
            ReleasedKeyActions = new Dictionary<Keys, NotifiableCollection<Action>>();
            actionIterator = new DynamicIterator<Action>();
        }

        public override void Initialize(GameItem gameItem)
        {
            gameItem.Disposed += () => { DisposeCurrentKeypoard(); };

            gameItem.UpdateLogic += (GameTime gameTime) => { keyboard.Update(gameTime); };
        }

        public override void Deserialize(XElement xmlProperties)
        {
            KeyboardType = xmlProperties.Attribute("KeyboardType").Value;
        }

        public override void Serialize() { }

        public void Update(GameTime gameTime) { }

        public void RegisterKeyPressed(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                if (!PressedKeyActions.ContainsKey(key))
                    PressedKeyActions.Add(key, new NotifiableCollection<Action>());

                PressedKeyActions[key].Add(action);
            }
        }

        public void UnregisterKeyPressed(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                PressedKeyActions[key].Remove(action);

                if (PressedKeyActions[key].Count == 0)
                    PressedKeyActions.Remove(key);
            }
        }

        public void RegisterKeyReleased(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                if (!ReleasedKeyActions.ContainsKey(key))
                    ReleasedKeyActions.Add(key, new NotifiableCollection<Action>());

                ReleasedKeyActions[key].Add(action);
            }
        }

        public void UnregisterKeyReleased(Action action, params Keys[] keys)
        {
            foreach (Keys key in keys)
            {
                ReleasedKeyActions[key].Remove(action);

                if (ReleasedKeyActions[key].Count == 0)
                    ReleasedKeyActions.Remove(key);
            }
        }

        public bool GetKeyState(Keys key, bool isKeyDown)
        {
            return keyboard.GetKeyState(key, isKeyDown);
        }

        private void CreateKeyboard(string keyboardType)
        {
            if (keyboardType == PlayerKeyboard.Name)
                keyboard = new PlayerKeyboard();
            else if (keyboardType == ArtificialKeyboard.Name)
                keyboard = new ArtificialKeyboard();
            else
                return;

            keyboard.KeysPressed += new Action<Keys[]>(Keyboard_KeysPressed);
            keyboard.KeysReleased += new Action<Keys[]>(Keyboard_KeysReleased);
        }

        private void DisposeCurrentKeypoard()
        {
            if (keyboard == null)
                return;

            keyboard.KeysPressed -= new Action<Keys[]>(Keyboard_KeysPressed);
            keyboard.KeysReleased -= new Action<Keys[]>(Keyboard_KeysReleased);
            keyboard = null;
        }

        private void ExecuteActions(Keys[] keys, Dictionary<Keys, NotifiableCollection<Action>> actionDictionary)
        {
            foreach (Keys key in keys)
            {
                try { actionIterator.RegisterCollection(actionDictionary[key]); } catch { continue; }

                while (actionIterator.HasMore)
                    actionIterator.Next().Invoke();
            }
        }

        public string KeyboardType
        {
            get { return keyboard.GetType().Name; }
            set
            {
                if (keyboard != null && keyboard.GetType().Name == value)
                    return;

                DisposeCurrentKeypoard();
                CreateKeyboard(value);
            }
        }

        private void Keyboard_KeysReleased(Keys[] keys)
        {
            ExecuteActions(keys, ReleasedKeyActions);
        }

        private void Keyboard_KeysPressed(Keys[] keys)
        {
            ExecuteActions(keys, PressedKeyActions);
        }
    }
}
